// @Author EthanScriptOn
// @Desc
package filter

import (
	"context"
	"fmt"
	"gitee.com/fatzeng/srf_switch/core/switch_subject"
	"gitee.com/fatzeng/srf_switch/core/switch_subject/statistics"
	"gitee.com/fatzeng/srf_switch_basic_components/switch_log"
	"reflect"
)

type DefaultSrfSwitchFilter struct {
	middleware   switch_subject.CacheMiddleware
	ruleFilter   switch_subject.Filter
	factorSwitch switch_subject.Filter
}

func GenerateDefaultSrfSwitchFilter(ruleFilter switch_subject.Filter, middleware switch_subject.CacheMiddleware, factorSwitch switch_subject.Filter) *DefaultSrfSwitchFilter {
	return &DefaultSrfSwitchFilter{
		middleware:   middleware,
		ruleFilter:   ruleFilter,
		factorSwitch: factorSwitch,
	}
}

func (d *DefaultSrfSwitchFilter) Filter(ctx context.Context, rule interface{}) bool {
	value := ctx.Value(switch_subject.STATISTIC)
	statisticItem := value.(*statistics.SwitchStatistic)
	ruleValue := reflect.ValueOf(rule)
	if ruleValue.Kind() == reflect.Ptr && !ruleValue.IsNil() {
		ruleValue = ruleValue.Elem()
	}
	srfRule, ok := ruleValue.Interface().(*switch_subject.SrfRule)
	if !ok {
		statisticItem.SwitchName = "DefaultName"
		switch_log.Logger().Warn("rule is not SrfRule")
		return false
	}
	isHitCache, ruleState := d.middleware.IsHitCache(ctx, srfRule)
	if isHitCache {
		statisticItem.Remark = "hit middleware!"
		return ruleState
	}
	finalRuleState := false
	relationship := srfRule.Relationship
	if relationship == switch_subject.OR {
		finalRuleState = d.getBoolean(ctx, true, srfRule)
	} else if relationship == switch_subject.AND {
		finalRuleState = d.getBoolean(ctx, false, srfRule)
	}
	statisticItem.IsPassThrough = finalRuleState
	switch_log.Logger().Info(fmt.Sprintf("finalRuleState: [%v]", finalRuleState))
	d.middleware.PutCache(ctx, srfRule, finalRuleState)
	return finalRuleState
}

func (d *DefaultSrfSwitchFilter) getBoolean(ctx context.Context, orAndGroup bool, srfRule *switch_subject.SrfRule) bool {
	if srfRule != nil {
		nestBoolean := d.ruleFilter.Filter(ctx, srfRule)
		if (orAndGroup && nestBoolean) || (!orAndGroup && !nestBoolean) {
			return orAndGroup
		}
	}
	if len(srfRule.FactorRules) <= 0 {
		return true
	}
	for _, factorRule := range srfRule.FactorRules {
		if orAndGroup == d.factorSwitch.Filter(ctx, factorRule) {
			return orAndGroup
		}
	}
	return !orAndGroup
}
